home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / cpp_libs / intrvews / xgrab.lha / xgrab / ui / pexec.c < prev    next >
C/C++ Source or Header  |  1990-03-06  |  51KB  |  2,433 lines

  1. /**
  2.    GRAB Graph Layout and Browser System
  3.  
  4.    Copyright (c) 1989, Tera Computer Company
  5.  **/
  6.  
  7.   /**
  8.      Execute predicates.  Predicates are executed directly from the
  9.      parse tree, which means semantic checking is done multiple times
  10.      on some pieces, maybe never on others.  
  11.    **/
  12.  
  13. #include <string.h>
  14. #include "attribute.h"
  15. #include "digraph.h"
  16. #include "pexec.h"
  17. #include "token.h"
  18. #include "pparse.h"
  19. #include "globals.h"
  20.  
  21. BOOL exec_error;
  22. static BOOL pchanged, playout, pnewattr, pfocus;
  23. static BOOL pbroken, pcontinued;    
  24.   /* indication of a 'break' or 'continue' command */
  25. static NODE *foc_node;
  26.  
  27. DIGRAPH *copy_digraph();
  28. LELEMENT *all_nodes_edges();
  29. char *get_val(), *get_nattr_val(), *get_eattr_val();
  30. BOOL get_bool_val();
  31. BOOL check_true(), check_equal();
  32. LELEMENT *get_elem();
  33. LELEMENT *find_elem(), *find_relative();
  34. LELEMENT *get_elems(), *find_relatives();
  35. LELEMENT *get_inedges(), *get_outedges(), *get_parents(), *get_children();
  36. LELEMENT *get_ancestors(), *get_descendants();
  37. LELEMENT *get_source(), *get_sink();
  38. LELEMENT *oedges(), *iedges();
  39. BRUSH string_to_brush();
  40. SHAPE string_to_shape();
  41. COLOR string_to_color();
  42. char *shape_to_string(), *color_to_string(), *brush_to_string();
  43. char *bool_to_string();
  44. OUTEDGE *get_edge();
  45. NODE *visual();
  46. char *UniqueName();
  47.  
  48. typedef LELEMENT * (*PFT1)();
  49. typedef LELEMENT * (*PFT2)();
  50.  
  51.   /* node relative functions */
  52. PFT1 nrelfn [NUM_NREL] = 
  53. {
  54.     get_inedges,
  55.     get_outedges,
  56.     get_parents,
  57.     get_children,
  58.     get_ancestors,
  59.     get_descendants
  60. };
  61.  
  62.   /* edge relative functions */
  63. PFT2 erelfn[NUM_EREL] =
  64. {
  65.     get_source,
  66.     get_sink
  67. };
  68.  
  69. dispose_ptree(tree)
  70. PTREE *tree;
  71. {
  72.     if (tree != NULL)
  73.     {
  74.         dispose_ptree(tree->left);
  75.     dispose_ptree(tree->right);
  76.  
  77.     if (tree->val != NULL)
  78.     {
  79.         dispose(tree->val);
  80.     }
  81.  
  82.     dispose(tree);
  83.     }
  84. }
  85.  
  86. exec_commands(commands, err, changed, relayout, newattr, focus, fnode)
  87. PTREE *commands;
  88. BOOL *err, *changed, *relayout, *newattr, *focus;
  89. NODE **fnode;
  90.   /**
  91.      Execute the top-level list of commands.
  92.      When done, err is TRUE if there was an error.
  93.         changed is TRUE if the graph was changed
  94.         relayout is TRUE if the graph needs to be relayedout
  95.         newattr is TRUE if a new attribute was added
  96.         focus is TRUE if a node was focussed to
  97.         fnode is the node to focus to
  98.    **/
  99. {
  100.     DIGRAPH *save, *tmp;  /* in case things fail */
  101.     LELEMENT *elems, *list; 
  102.       /* the list of things to execute the commands upon, and a copy of it */
  103.  
  104.     pchanged = FALSE;
  105.     playout = FALSE;
  106.     pnewattr = FALSE;
  107.     pfocus = FALSE;
  108.     *fnode = NULL;
  109.     exec_error = FALSE;
  110.     pbroken = FALSE;
  111.     tmp = copy_digraph(digraph);
  112.     save = digraph;
  113.     digraph = tmp;
  114.  
  115.     for (elems = all_nodes_edges(digraph), list = elems; elems != NULL; 
  116.      elems = elems->next)
  117.     {
  118.     pcontinued = FALSE;
  119.     exec_command_list(commands, digraph, elems);
  120.  
  121.     if (exec_error)
  122.     {
  123.         goto resave;
  124.     }
  125.     else if (pbroken)
  126.     {
  127.         break;
  128.     }
  129.     }
  130.  
  131.     *err = FALSE;
  132.     *changed = pchanged;
  133.     *relayout = playout;
  134.     *newattr = pnewattr;
  135.     *focus = pfocus;
  136.     *fnode = foc_node;
  137.  
  138.     dispose_lelem_list(list);
  139.  
  140.     if (pchanged)
  141.     {
  142.     dispose_digraph(save);
  143.     }
  144.     else     /* no change, so keep the old digraph */
  145.     {
  146.     dispose_digraph(digraph);
  147.     digraph = save;
  148.     }
  149.  
  150.     return;
  151.  
  152. resave:
  153.     fprintf(stderr, "Old digraph retained.\n");
  154.     dispose_digraph(digraph);
  155.     digraph = save;
  156.     *changed = FALSE;
  157.     *relayout = FALSE;
  158.     *newattr = FALSE;
  159.     *err = TRUE;
  160.     *focus = FALSE;
  161.     *fnode = NULL;
  162.  
  163.     dispose_lelem_list(list);
  164.     return;
  165. }
  166.  
  167. dispose_lelem_list(l)
  168. LELEMENT *l;
  169. {
  170.     LELEMENT *next;
  171.  
  172.     while (l != NULL)
  173.     {
  174.     next = l->next;
  175.     dispose(l);
  176.     l = next;
  177.     }
  178. }
  179.  
  180. exec_command_list(command, digraph, current)
  181. PTREE *command;
  182. DIGRAPH *digraph;
  183. LELEMENT *current;
  184.   /**
  185.      execute the commands in command on digraph digraph, where current is the
  186.      current node.
  187.    **/
  188. {
  189.     PTREE *currcommand;
  190.     NODE *to;
  191.  
  192.     for (currcommand = command; currcommand != NULL; 
  193.          currcommand = currcommand->right)
  194.     {
  195.     if (!current->node)
  196.       /* find the destination if it's an outedge */
  197.     {
  198.         to = visual(digraph, 
  199.             Node(digraph, To_vno((OUTEDGE *) current->gelement)));
  200.     }
  201.  
  202.     if (ignoreHidden &&
  203.         (current->node && !Displayed((NODE *) current->gelement)) ||
  204.         (!current->node && 
  205.          (!Displayed(current->from) || !Displayed(to))))
  206.       /* stop processing if the node or edge has become hidden */
  207.     {
  208.         break;
  209.     }
  210.  
  211.         if (currcommand->token != SEMICOLON_TOKEN)
  212.         {
  213.             exec_error = TRUE;
  214.         print_ln(currcommand->lineno);
  215.         print_cl(current);
  216.         fprintf(stderr, "exec_command_lists: not a command list ");
  217.         print_token(currcommand->token, TRUE);
  218.         return;
  219.     }
  220.     else
  221.     {
  222.         exec_command(currcommand->left, digraph, current);
  223.         exec_ck;
  224.     }
  225.     }
  226. }
  227.  
  228. exec_command(command, digraph, current)
  229. PTREE *command;
  230. DIGRAPH *digraph;
  231. LELEMENT *current;
  232. {
  233.     exec_ck;
  234.  
  235.     if (command != NULL)    /* a superfluous semicolon could cause this */
  236.     {
  237.     switch (command->token)
  238.     {
  239.         case IF_TOKEN:
  240.         exec_if_or_elsifstmt(command, digraph, current);
  241.         break;
  242.         case SET_TOKEN:
  243.         exec_setstmt(command, digraph, current);
  244.         break;
  245.         case COALESCE_TOKEN:
  246.         exec_coalescestmt(command, digraph, current);
  247.         break;
  248.         case EXPAND_TOKEN:
  249.         exec_expandstmt(command, digraph, current);
  250.         break;
  251.         case HIDE_TOKEN:
  252.         exec_hidestmt(command, digraph, current);
  253.         break;
  254.         case DISPLAY_TOKEN:
  255.         exec_displaystmt(command, digraph, current);
  256.         break;
  257.         case FOCUS_TOKEN:
  258.         exec_focusstmt(command, digraph, current);
  259.         break;
  260.         case BREAK_TOKEN:
  261.         pbroken = TRUE;
  262.         break;
  263.         case CONTINUE_TOKEN:
  264.         pcontinued = TRUE;
  265.         break;
  266.         default:
  267.         exec_error = TRUE;
  268.             print_ln(command->lineno);
  269.             print_cl(current);
  270.         fprintf(stderr, "exec_command: not a valid command");
  271.         print_token(command->token, TRUE);
  272.         break;
  273.     }
  274.     }
  275. }
  276.  
  277. exec_focusstmt(command, digraph, current)
  278. PTREE *command;
  279. DIGRAPH *digraph;
  280. LELEMENT *current;
  281. {
  282.     PTREE *rest;
  283.     LELEMENT *elems;
  284.  
  285.     exec_ck;
  286.  
  287.     if (command == NULL)
  288.     {
  289.     exec_error = TRUE;
  290.     print_cl(current);
  291.     fprintf(stderr, "node tree empty\n");
  292.     return;
  293.     }
  294.     else if (command->token != FOCUS_TOKEN)
  295.     {
  296.     exec_error = TRUE;
  297.     print_ln(command->lineno);
  298.     print_cl(current);
  299.     fprintf(stderr, "focus tree can't be rooted with ");
  300.     print_token(command->token, TRUE);
  301.     return;
  302.     }
  303.     else if (command->right != NULL)
  304.     {
  305.     exec_error = TRUE;
  306.     print_ln(command->lineno);
  307.     print_cl(current);
  308.     fprintf(stderr, "focus tree must have empty right child.\n");
  309.     return;
  310.     }
  311.     else 
  312.     {
  313.     elems = get_elems(command->left, digraph, current, &rest);
  314.     exec_ck;
  315.  
  316.     if (rest != NULL)
  317.     {
  318.         exec_error = TRUE;
  319.         print_ln(rest->lineno);
  320.         print_cl(current);
  321.         fprintf(stderr, "right child of focus node tree must be empty.\n");
  322.         return;
  323.     }
  324.     else 
  325.     {
  326.         for (; elems != NULL; elems = elems->next)
  327.         {
  328.         if (!elems->node)
  329.         {
  330.                 exec_error = TRUE;
  331.                 print_ln(command->lineno);
  332.                 print_cl(current);
  333.                 fprintf(stderr, 
  334.                 "Target of focus command must be a node.\n");
  335.                 return;
  336.         }
  337.             else
  338.             {
  339.             pfocus = TRUE;
  340.             foc_node = (NODE *) elems->gelement;
  341.         }
  342.         }
  343.     }
  344.     }
  345. }
  346.  
  347. exec_hidestmt(command, digraph, current)
  348. PTREE *command;
  349. DIGRAPH *digraph;
  350. LELEMENT *current;
  351.   /**
  352.      after a hide, the succ/ante sets must be changed.  This routine
  353.      doesn't change them.  The graph had better be relayed out, which will
  354.      fix the sets
  355.    **/
  356. {
  357.     PTREE *rest;
  358.     LELEMENT *elems;
  359.  
  360.     exec_ck;
  361.  
  362.     if (command == NULL)
  363.     {
  364.     exec_error = TRUE;
  365.     print_cl(current);
  366.     fprintf(stderr, "node tree empty\n");
  367.     return;
  368.     }
  369.     else if (command->token != HIDE_TOKEN)
  370.     {
  371.     exec_error = TRUE;
  372.     print_ln(command->lineno);
  373.     print_cl(current);
  374.     fprintf(stderr, "hide tree can't be rooted with ");
  375.     print_token(command->token, TRUE);
  376.     return;
  377.     }
  378.     else if (command->right != NULL)
  379.     {
  380.     exec_error = TRUE;
  381.     print_ln(command->lineno);
  382.     print_cl(current);
  383.     fprintf(stderr, "hide tree must have empty right child.\n");
  384.     return;
  385.     }
  386.     else 
  387.     {
  388.     elems = get_elems(command->left, digraph, current, &rest);
  389.     exec_ck;
  390.  
  391.     if (rest != NULL)
  392.     {
  393.         exec_error = TRUE;
  394.         print_ln(rest->lineno);
  395.         print_cl(current);
  396.         fprintf(stderr, "right child of hide node tree must be empty.\n");
  397.         return;
  398.     }
  399.     else 
  400.     {
  401.         for (; elems != NULL; elems = elems->next)
  402.         {
  403.         if (!elems->node)
  404.         {
  405.                 exec_error = TRUE;
  406.                 print_ln(command->lineno);
  407.                 print_cl(current);
  408.                 fprintf(stderr, "Target of hide command must be a node.\n");
  409.                 return;
  410.         }
  411.             else
  412.             {
  413.                 Displayed((NODE *) (elems->gelement)) = FALSE;
  414.                 playout = TRUE;
  415.                 pchanged = TRUE;
  416.         }
  417.         }
  418.     }
  419.     }
  420. }
  421.  
  422. exec_displaystmt(command, digraph, current)
  423. PTREE *command;
  424. DIGRAPH *digraph;
  425. LELEMENT *current;
  426.   /**
  427.      After a display, the ante/succ sets must be changed, and this routine
  428.      doesn't change them.  The graph had better be relayed out, which will
  429.      cause the sets to be fixed.
  430.    **/
  431. {
  432.     PTREE *rest;
  433.     LELEMENT *elems;
  434.  
  435.     exec_ck;
  436.  
  437.     if (command == NULL)
  438.     {
  439.     exec_error = TRUE;
  440.     print_cl(current);
  441.     fprintf(stderr, "node tree empty\n");
  442.     return;
  443.     }
  444.     else if (command->token != DISPLAY_TOKEN)
  445.     {
  446.     exec_error = TRUE;
  447.     print_ln(command->lineno);
  448.     print_cl(current);
  449.     fprintf(stderr, "display tree can't be rooted with ");
  450.     print_token(command->token, TRUE);
  451.     return;
  452.     }
  453.     else if (command->right != NULL)
  454.     {
  455.     exec_error = TRUE;
  456.     print_ln(command->lineno);
  457.     print_cl(current);
  458.     fprintf(stderr, "display tree must have empty right child.\n");
  459.     return;
  460.     }
  461.     else 
  462.     {
  463.     elems = get_elems(command->left, digraph, current, &rest);
  464.     exec_ck;
  465.  
  466.     if (rest != NULL)
  467.     {
  468.         exec_error = TRUE;
  469.         print_ln(rest->lineno);
  470.         print_cl(current);
  471.         fprintf(stderr, 
  472.             "right child of display node tree must be empty.\n");
  473.         return;
  474.     }
  475.     else 
  476.     {
  477.         for (; elems != NULL; elems = elems->next)
  478.         {
  479.         if (!elems->node)
  480.         {
  481.                 exec_error = TRUE;
  482.                 print_ln(command->lineno);
  483.                 print_cl(current);
  484.                 fprintf(stderr, 
  485.                 "Target of display command must be a node.\n");
  486.                 return;
  487.         }
  488.             else
  489.             {
  490.                 Displayed((NODE *) (elems->gelement)) = TRUE;
  491.                 playout = TRUE;
  492.                 pchanged = TRUE;
  493.             }
  494.         }
  495.     }
  496.     }
  497. }
  498.  
  499. exec_coalescestmt(command, digraph, current)
  500. PTREE *command;
  501. DIGRAPH *digraph;
  502. LELEMENT *current;
  503. {
  504.     PTREE *rest;
  505.     LELEMENT *elems;
  506.     LELEMENT *elem;
  507.  
  508.     exec_ck;
  509.  
  510.     if (command == NULL)
  511.     {
  512.     exec_error = TRUE;
  513.     print_cl(current);
  514.     fprintf(stderr, "node tree empty\n");
  515.     return;
  516.     }
  517.     else if (command->token != COALESCE_TOKEN)
  518.     {
  519.     exec_error = TRUE;
  520.     print_ln(command->lineno);
  521.     print_cl(current);
  522.     fprintf(stderr, "coalesce tree can't be rooted with ");
  523.     print_token(command->token, TRUE);
  524.     return;
  525.     }
  526.     else 
  527.     {
  528.     elems = get_elems(command->left, digraph, current, &rest);
  529.     exec_ck;
  530.  
  531.     if (rest != NULL)
  532.     {
  533.         exec_error = TRUE;
  534.         print_ln(rest->lineno);
  535.         print_cl(current);
  536.         fprintf(stderr, 
  537.             "right child of first coalesce node tree must be empty.\n");
  538.         return;
  539.     }
  540.  
  541.     elem = get_elem(command->right, digraph, current, &rest);
  542.     exec_ck;
  543.  
  544.     if (rest != NULL)
  545.     {
  546.         exec_error = TRUE;
  547.         print_ln(rest->lineno);
  548.         print_cl(current);
  549.         fprintf(stderr, "right child of second coalesce node tree must be empty.\n");
  550.         return;
  551.     }
  552.     else
  553.     {
  554.         if (!elem->node)
  555.         {
  556.         exec_error = TRUE;
  557.         print_ln(command->lineno);
  558.         print_cl(current);
  559.         fprintf(stderr, "Second coalesce argument must be a node.\n");
  560.         return;
  561.         }
  562.         else
  563.         {
  564.           /**
  565.              first off, get a coalesced node.  We do this by 
  566.              coalescing the node with itself (which does nothing if
  567.              the node is already coalesced.  If it isn't, it creates 
  568.              a new coalesced node.  Find the new node by using 
  569.              find_elem
  570.            **/
  571.         coalesce(digraph, (NODE *) elem->gelement, 
  572.              (NODE *) elem->gelement);
  573.         elem = find_elem(Text((NODE *) elem->gelement), digraph, 
  574.                     command->lineno, current);
  575.  
  576.           /**
  577.              now coalesce the set of nodes with this one
  578.            **/
  579.             for (; elems != NULL; elems = elems->next)
  580.             {
  581.                 if (!elems->node)
  582.             {
  583.                     exec_error = TRUE;
  584.                     print_ln(command->lineno);
  585.                     print_cl(current);
  586.                     fprintf(stderr, "First coalesce argument must be a set of nodes.\n");
  587.                     return;
  588.             }
  589.                 else
  590.                 {
  591.                 coalesce(digraph,
  592.                  (NODE *) elems->gelement,
  593.                  (NODE *) elem->gelement);
  594.                     playout = TRUE;
  595.                     pchanged = TRUE;
  596.             }
  597.             }
  598.         }
  599.     }
  600.     }
  601. }
  602.  
  603. exec_expandstmt(command, digraph, current)
  604. PTREE *command;
  605. DIGRAPH *digraph;
  606. LELEMENT *current;
  607. {
  608.     PTREE *rest;
  609.     LELEMENT *elems;
  610.  
  611.     exec_ck;
  612.  
  613.     if (command == NULL)
  614.     {
  615.     exec_error = TRUE;
  616.     print_cl(current);
  617.     fprintf(stderr, "node tree empty\n");
  618.     return;
  619.     }
  620.     else if (command->token != EXPAND_TOKEN)
  621.     {
  622.     exec_error = TRUE;
  623.     print_ln(command->lineno);
  624.     print_cl(current);
  625.     fprintf(stderr, "expand tree can't be rooted with ");
  626.     print_token(command->token, TRUE);
  627.     return;
  628.     }
  629.     else if (command->right != NULL)
  630.     {
  631.     exec_error = TRUE;
  632.     print_ln(command->lineno);
  633.     print_cl(current);
  634.     fprintf(stderr, "expand tree must have empty right child.\n");
  635.     return;
  636.     }
  637.     else 
  638.     {
  639.     elems = get_elems(command->left, digraph, current, &rest);
  640.     exec_ck;
  641.  
  642.     if (rest != NULL)
  643.     {
  644.         exec_error = TRUE;
  645.         print_ln(rest->lineno);
  646.         print_cl(current);
  647.         fprintf(stderr, 
  648.             "right child of expand node tree must be empty.\n");
  649.         return;
  650.     }
  651.     else
  652.     {
  653.         for (; elems != NULL; elems = elems->next)
  654.         {
  655.             if (!elems->node)
  656.         {
  657.                 exec_error = TRUE;
  658.                 print_ln(command->lineno);
  659.                 print_cl(current);
  660.                 fprintf(stderr, 
  661.                 "Target of expand command must be a node.\n");
  662.                 return;
  663.         }
  664.             else
  665.             {
  666.                 if (!Coalescer((NODE *) elems->gelement))
  667.                 {
  668. print_ln(command->left->lineno);
  669. print_cl(current);
  670. fprintf(stderr, "warning: expand on a non-coalescer node\n");
  671.             }
  672.             else
  673.             {
  674.                 expand(digraph, (NODE *) elems->gelement);
  675.                     playout = TRUE;
  676.                     pchanged = TRUE;
  677.             }
  678.             }
  679.         }
  680.     }
  681.     }
  682. }
  683.  
  684. exec_setstmt(command, digraph, current)
  685. PTREE *command;
  686. DIGRAPH *digraph;
  687. LELEMENT *current;
  688. {
  689.     char *val;
  690.  
  691.     exec_ck;
  692.  
  693.     val = get_val(command->right, digraph, current);
  694.     set_attrs(command->left, digraph, current, val);
  695. }
  696.  
  697. exec_if_or_elsifstmt(command, digraph, current)
  698. PTREE *command;
  699. DIGRAPH *digraph;
  700. LELEMENT *current;
  701. {
  702.     BOOL val;
  703.  
  704.     exec_ck;
  705.  
  706.     if (command == NULL)
  707.       /* end of if-elsif chain; no else statement */
  708.     {
  709.     return;
  710.     }
  711.     else if (command->token == SEMICOLON_TOKEN)
  712.       /* else statement: command list */
  713.     {
  714.     exec_command_list(command, digraph, current);
  715.     }
  716.     else if (command->left == NULL)
  717.     {
  718.     exec_error = TRUE;
  719.     print_ln(command->lineno);
  720.     print_cl(current);
  721.     fprintf(stderr, "exec_if_or_elsifstmt: empty then tree\n");
  722.     return;
  723.     }
  724.     else if (command->left->token != THEN_TOKEN)
  725.     {
  726.     exec_error = TRUE;
  727.     print_ln(command->left->lineno);
  728.     print_cl(current);
  729.     fprintf(stderr, "exec_ifstmt: then tree can't be rooted with ");
  730.     print_token(command->left->token, TRUE);
  731.     return;
  732.     }
  733.     else
  734.     {
  735.     val = check_true(command->left->left, digraph, current);
  736.     exec_ck;
  737.  
  738.         if (val)
  739.     {
  740.             exec_command_list(command->left->right, digraph, current);
  741.     }
  742.     else
  743.     {
  744.         exec_if_or_elsifstmt(command->right, digraph, current);
  745.     }
  746.     }
  747. }
  748.  
  749. BOOL check_true(bexpr, digraph, current)
  750. PTREE *bexpr;
  751. DIGRAPH *digraph;
  752. LELEMENT *current;
  753. {
  754.     exec_ckb;
  755.  
  756.     if (bexpr == NULL)
  757.     {
  758.     exec_error = TRUE;
  759.     print_cl(current);
  760.     fprintf(stderr, "boolean expression tree empty.\n");
  761.     return FALSE;
  762.     }
  763.     else
  764.     {
  765.     BOOL val;
  766.  
  767.     switch (bexpr->token)
  768.     {
  769.         case AND_TOKEN:
  770.         val = check_true(bexpr->left, digraph, current);
  771.         exec_ckb;
  772.  
  773.         return (val && check_true(bexpr->right, digraph, current));
  774.  
  775.         case OR_TOKEN:
  776.         val = check_true(bexpr->left, digraph, current);
  777.         exec_ckb;
  778.  
  779.         return (val || check_true(bexpr->right, digraph, current));
  780.  
  781.         case NOT_TOKEN:
  782.         return !check_true(bexpr->left, digraph, current);
  783.  
  784.         case EQUAL_TOKEN:
  785.           /**
  786.              EQUAL_TOKEN stands for two types of expressions:
  787.             expr        (return TRUE if expr TRUE)
  788.             expr = expr    (return TRUE if expr = expr)
  789.              the former is indicated if the right child is NULL
  790.            **/
  791.         if (bexpr->right == NULL)
  792.         {
  793.             return get_bool_val(bexpr->left, digraph, current);
  794.         }
  795.         else
  796.         {
  797.             return check_equal(bexpr->left, bexpr->right, digraph, 
  798.                        current);
  799.         }
  800.         default:
  801.         exec_error = TRUE;
  802.         print_ln(bexpr->lineno);
  803.         print_cl(current);
  804.         fprintf(stderr, 
  805.             "boolean expression tree cannot be rooted with ");
  806.         print_token(bexpr->token, TRUE);
  807.         return FALSE;
  808.     }
  809.     }
  810. }
  811.     
  812. BOOL check_equal(e1, e2, digraph, current)
  813. PTREE *e1, *e2;
  814. DIGRAPH *digraph;
  815. LELEMENT *current;
  816. {
  817.     char *s1, *s2;
  818.  
  819.     exec_ckb;
  820.  
  821.     s1 = get_val(e1, digraph, current);
  822.     exec_ckb;
  823.  
  824.     s2 = get_val(e2, digraph, current);
  825.     exec_ckb;
  826.  
  827.     if (!strcmp(s1, s2))
  828.     {
  829.     return TRUE;
  830.     }
  831.     else
  832.     {
  833.     return FALSE;
  834.     }
  835. }
  836.  
  837. set_attrs(lval, digraph, current, val)
  838. PTREE *lval;
  839. DIGRAPH *digraph;
  840. LELEMENT *current;
  841. char *val;
  842.   /**
  843.      Set the attribute(s) (could be more than one) indicated by lval to val
  844.    **/
  845. {
  846.     PTREE *rest;
  847.     LELEMENT *elems;
  848.  
  849.     exec_ck;
  850.  
  851.     if (lval == NULL)
  852.     {
  853.     exec_error = TRUE;
  854.     print_cl(current);
  855.     fprintf(stderr, "lvalue tree empty\n");
  856.     return;
  857.     }
  858.     else 
  859.     {
  860.     elems = get_elems(lval, digraph, current, &rest);
  861.     exec_ck;
  862.  
  863.     for (; elems != NULL; elems = elems->next)
  864.     {
  865.         if (elems->node)
  866.         {
  867.             set_nattr(digraph, (NODE *) (elems->gelement), rest, current, 
  868.               val);
  869.         exec_ck;
  870.         }
  871.         else
  872.         {
  873.             set_eattr(digraph, (OUTEDGE *) (elems->gelement), rest, 
  874.               current, val);
  875.         exec_ck;
  876.         }
  877.     }
  878.     }
  879. }
  880.  
  881. char *get_val(rval, digraph, current)
  882. PTREE *rval;
  883. DIGRAPH *digraph;
  884. LELEMENT *current;
  885. {
  886.     PTREE *rest;
  887.     LELEMENT *elem;
  888.  
  889.     exec_ckp;
  890.  
  891.     if (rval == NULL)
  892.     {
  893.     exec_error = TRUE;
  894.     print_cl(current);
  895.     fprintf(stderr, "rvalue tree empty\n");
  896.     return NULL;
  897.     }
  898.     else if (rval->token == QSTRING_TOKEN)
  899.       /* quoted strings indicate literal values */
  900.     {
  901.     return rval->val;
  902.     }
  903.     else
  904.     {
  905.         elem = get_elem(rval, digraph, current, &rest);
  906.     exec_ckp;
  907.  
  908.     if (elem->node)
  909.     {
  910.         return get_nattr_val(digraph, (NODE *) (elem->gelement), rest, 
  911.                  current);
  912.     }
  913.     else
  914.     {
  915.         return get_eattr_val(digraph, (OUTEDGE *) (elem->gelement), rest, 
  916.                  current);
  917.     }
  918.     }
  919. }
  920.  
  921. LELEMENT *get_elem(tree, digraph, current, rest)
  922. PTREE *tree;
  923. DIGRAPH *digraph;
  924. LELEMENT *current;
  925. PTREE **rest;
  926.   /* get *one* element of the graph indicated by tree */
  927. {
  928.     LELEMENT *curr, *tmp;
  929.  
  930.     exec_ckp;
  931.  
  932.     if (tree == NULL)
  933.     {
  934.         exec_error = TRUE;
  935.     print_cl(current);
  936.         fprintf(stderr, "node or edge designator tree empty.\n");
  937.         return NULL;
  938.     }
  939.     else if (tree->token != PERIOD_TOKEN)
  940.     {
  941.     exec_error = TRUE;
  942.     print_ln(tree->lineno);
  943.     print_cl(current);
  944.     fprintf(stderr, "node or edge designator tree can't be rooted with ");
  945.     print_token(tree->token, TRUE);
  946.     return NULL;
  947.     }
  948.     else if (tree->left == NULL)
  949.       /* start at current node */
  950.     {
  951.     new(curr, LELEMENT);
  952.     curr->node = current->node;
  953.     curr->gelement = current->gelement;
  954.     curr->from = current->from;
  955.     curr->next = NULL;
  956.     }
  957.     else if (tree->left->token != QSTRING_TOKEN)
  958.     {
  959.     exec_error = TRUE;
  960.     print_ln(tree->left->lineno);
  961.     print_cl(current);
  962.     fprintf(stderr, "attribute designator tree can't be rooted with ");
  963.     print_token(tree->left->token, TRUE);
  964.     return NULL;;
  965.     }
  966.     else
  967.     {
  968.     curr = find_elem(tree->left->val, digraph, tree->left->lineno, current);
  969.     }
  970.  
  971.     for (tree = tree->right; tree != NULL && tree->token == PERIOD_TOKEN; 
  972.      tree = tree->right)
  973.     {
  974.     tmp = find_relative(curr, tree->left, digraph, current);
  975.       dispose(curr);
  976.     curr = tmp;
  977.     exec_ckp;
  978.     }
  979.  
  980.     *rest = tree;
  981.  
  982.     return curr;
  983. }
  984.  
  985. LELEMENT *find_relative(curr, tree, digraph, current)
  986. LELEMENT *curr;
  987. PTREE *tree;
  988. DIGRAPH *digraph;
  989. LELEMENT *current;
  990.   /* find *one* relative of curr indicated by tree */
  991. {
  992.     char *s;
  993.     LELEMENT *relatives;
  994.  
  995.     exec_ckp;
  996.  
  997.     if (tree == NULL)
  998.     {
  999.     exec_error = TRUE;
  1000.     print_cl(current);
  1001.     fprintf(stderr, "attribute designator tree is empty\n");
  1002.     return NULL;
  1003.     }
  1004.     else
  1005.     {
  1006.     switch (tree->token)
  1007.     {
  1008.         case STRING_TOKEN:
  1009.         if (curr->node)
  1010.         {
  1011.                 s = get_nattr_val(digraph, (NODE *) (curr->gelement),
  1012.                       tree, current);
  1013.             exec_ckp;
  1014.             }
  1015.         else
  1016.         {
  1017.                 s = get_eattr_val(digraph, (OUTEDGE *) (curr->gelement), 
  1018.                       tree, current);
  1019.             exec_ckp;
  1020.             }
  1021.  
  1022.         return find_elem(s, digraph, tree->lineno, current);
  1023.     
  1024.         case IN_TOKEN:
  1025.         case OUT_TOKEN:
  1026.         case PARENTS_TOKEN:
  1027.         case CHILDREN_TOKEN:
  1028.         case ANCESTORS_TOKEN:
  1029.         case DESCENDANTS_TOKEN:
  1030.         if (!curr->node)
  1031.         {
  1032.             exec_error = TRUE;
  1033.             print_ln(tree->lineno);
  1034.             print_cl(current);
  1035.             fprintf(stderr, "target of %s attribute not a node\n", 
  1036.                 reserved_wd[(int) tree->token - 
  1037.                     (int) FIRST_RESERVED_TOKEN]);
  1038.             return NULL;
  1039.         }
  1040.         else
  1041.         {
  1042.             relatives = 
  1043.               nrelfn[(int) tree->token - 
  1044.                  (int) FIRST_NREL_TOKEN]((NODE *) (curr->gelement),
  1045.                              digraph);
  1046.  
  1047.             if (relatives == NULL)
  1048.             {
  1049.             exec_error = TRUE;
  1050.                 print_ln(tree->lineno);
  1051.                 print_cl(current);
  1052.             fprintf(stderr, "%s attribute yields empty set\n",
  1053.                     reserved_wd[(int) tree->token -
  1054.                         (int) FIRST_RESERVED_TOKEN]);
  1055.             return NULL;
  1056.             }
  1057.             else if (relatives->next != NULL)
  1058.             {
  1059.             exec_error = TRUE;
  1060.                 print_ln(tree->lineno);
  1061.                 print_cl(current);
  1062.             fprintf(stderr, "%s attribute result ambiguous\n",
  1063.                     reserved_wd[(int) tree->token - 
  1064.                         (int) FIRST_RESERVED_TOKEN]);
  1065.             dispose_lelem_list(relatives);
  1066.             return NULL;
  1067.             }
  1068.             else
  1069.             {
  1070.             return relatives;
  1071.             }
  1072.         }
  1073.     
  1074.         case SOURCE_TOKEN:
  1075.         case SINK_TOKEN:
  1076.         if (curr->node)
  1077.         {
  1078.             exec_error = TRUE;
  1079.             print_ln(tree->lineno);
  1080.             print_cl(current);
  1081.             fprintf(stderr, "target of %s attribute not an edge\n",
  1082.                 reserved_wd[(int) tree->token - 
  1083.                     (int) FIRST_RESERVED_TOKEN]);
  1084.             return NULL;
  1085.         }
  1086.         else
  1087.         {
  1088.             relatives = 
  1089.               erelfn[(int) tree->token -
  1090.                  (int) FIRST_EREL_TOKEN](
  1091.                    (OUTEDGE *) (curr->gelement), digraph, 
  1092.                    curr->from);
  1093.  
  1094.             if (relatives == NULL)
  1095.             {
  1096.             exec_error = TRUE;
  1097.                 print_ln(tree->lineno);
  1098.                 print_cl(current);
  1099.             fprintf(stderr, "%s attribute yields empty set\n",
  1100.                     reserved_wd[(int) tree->token -
  1101.                         (int) FIRST_RESERVED_TOKEN]);
  1102.             return NULL;
  1103.             }
  1104.             else if (relatives->next != NULL)
  1105.             {
  1106.             exec_error = TRUE;
  1107.                 print_ln(tree->lineno);
  1108.                 print_cl(current);
  1109.             fprintf(stderr, "%s attribute result ambiguous\n",
  1110.                     reserved_wd[(int) tree->token - 
  1111.                         (int) FIRST_RESERVED_TOKEN]);
  1112.             dispose_lelem_list(relatives);
  1113.             return NULL;
  1114.             }
  1115.             else
  1116.             {
  1117.             return relatives;
  1118.             }
  1119.         }
  1120.  
  1121.         default:
  1122.         exec_error = TRUE;
  1123.         print_ln(tree->lineno);
  1124.         print_cl(current);
  1125.         fprintf(stderr, 
  1126.             "attribute designator tree can't be rooted with ");
  1127.         print_token(tree->token, TRUE);
  1128.         return NULL;
  1129.     }
  1130.     }
  1131. }
  1132.  
  1133. LELEMENT *find_elem(s, digraph, lineno, current)
  1134. char *s;
  1135. DIGRAPH *digraph;
  1136. int lineno;
  1137. LELEMENT *current;
  1138.   /**
  1139.      find and return the first element in the digraph with distinguished 
  1140.      attribute equal to s.  
  1141.    **/
  1142. {
  1143.     NODE *node;
  1144.     LELEMENT *result, *p;
  1145.  
  1146.     exec_ckp;
  1147.  
  1148.     each_pred_node(digraph, node)
  1149.     loop
  1150.     if (Is_dummy(node))
  1151.     {
  1152.         continue;
  1153.     }
  1154.  
  1155.     if (!strcmp(s, Text(node)))
  1156.     {
  1157.         new(result, LELEMENT);
  1158.         result->node = TRUE;
  1159.             result->from = NULL;
  1160.         result->gelement = (GELEMENT) node;
  1161.         result->next = NULL;
  1162.         return result;
  1163.     }
  1164.     else
  1165.     {
  1166.         result = oedges(digraph, node);
  1167.  
  1168.         for (; result != NULL; result = p)
  1169.         {
  1170.         if (!strcmp(s, Label((OUTEDGE *) result->gelement)))
  1171.         {
  1172.             dispose_lelem_list(result->next);
  1173.             result->next = NULL;
  1174.             return result;
  1175.         }
  1176.         else
  1177.         {
  1178.             p = result->next;
  1179.             dispose(result);
  1180.         }
  1181.         }
  1182.     }
  1183.     endloop;
  1184.  
  1185.     exec_error = TRUE;
  1186.     print_ln(lineno);
  1187.     print_cl(current);
  1188.     fprintf (stderr, "Couldn't find node or edge with label %s\n", s);
  1189.     return NULL;
  1190. }
  1191.  
  1192. LELEMENT *get_elems(tree, digraph, current, rest)
  1193. PTREE *tree;
  1194. DIGRAPH *digraph;
  1195. LELEMENT *current;
  1196. PTREE **rest;
  1197.   /**
  1198.      find the element(s) (>=0 of them) indicated by tree
  1199.    **/
  1200. {
  1201.     LELEMENT *curr, *tmp;
  1202.  
  1203.     exec_ckp;
  1204.  
  1205.     if (tree == NULL)
  1206.     {
  1207.         exec_error = TRUE;
  1208.     print_cl(current);
  1209.         fprintf(stderr, "node or edge designator tree empty.\n");
  1210.         return NULL;
  1211.     }
  1212.     else if (tree->token != PERIOD_TOKEN)
  1213.     {
  1214.     exec_error = TRUE;
  1215.     print_ln(tree->lineno);
  1216.     print_cl(current);
  1217.     fprintf(stderr, "node or edge designator tree can't be rooted with ");
  1218.     print_token(tree->token, TRUE);
  1219.     return NULL;
  1220.     }
  1221.     else if (tree->left == NULL)
  1222.       /* start at current node */
  1223.     {
  1224.     new(curr, LELEMENT);
  1225.     curr->node = current->node;
  1226.     curr->gelement = current->gelement;
  1227.     curr->from = current->from;
  1228.     curr->next = NULL;
  1229.     }
  1230.     else if (tree->left->token != QSTRING_TOKEN)
  1231.     {
  1232.     exec_error = TRUE;
  1233.     print_ln(tree->left->lineno);
  1234.     print_cl(current);
  1235.     fprintf(stderr, "attribute designator tree can't be rooted with ");
  1236.     print_token(tree->left->token, TRUE);
  1237.     return NULL;
  1238.     }
  1239.     else
  1240.     {
  1241.     curr = find_elem(tree->left->val, digraph, tree->left->lineno, 
  1242.               current);
  1243.     }
  1244.  
  1245.     for (tree = tree->right; tree != NULL && tree->token == PERIOD_TOKEN; 
  1246.      tree = tree->right)
  1247.     {
  1248.     tmp = find_relatives(curr, tree->left, digraph, current);
  1249.     dispose_lelem_list(curr);
  1250.     curr = tmp;
  1251.     exec_ckp;
  1252.     }
  1253.  
  1254.     *rest = tree;
  1255.     return curr;
  1256. }
  1257.  
  1258. LELEMENT *find_relatives(curr, tree, digraph, current)
  1259. LELEMENT *curr;
  1260. PTREE *tree;
  1261. DIGRAPH *digraph;
  1262. LELEMENT *current;
  1263.   /**
  1264.      find the relative(s) (>= 0) of curr indicated by tree
  1265.    **/
  1266. {
  1267.     char *s;
  1268.     LELEMENT *relatives, *head;
  1269.  
  1270.     exec_ckp;
  1271.  
  1272.     if (tree == NULL)
  1273.     {
  1274.     exec_error = TRUE;
  1275.     print_cl(current);
  1276.     fprintf(stderr, "attribute designator tree is empty\n");
  1277.     return NULL;
  1278.     }
  1279.  
  1280.     for (head = NULL; curr != NULL; curr = curr->next)
  1281.     {
  1282.     switch (tree->token)
  1283.     {
  1284.         case STRING_TOKEN:
  1285.         if (curr->node)
  1286.         {
  1287.                 s = get_nattr_val(digraph, (NODE *) (curr->gelement), 
  1288.                       tree, current);
  1289.             exec_ckp;
  1290.             }
  1291.         else
  1292.         {
  1293.                 s = get_eattr_val(digraph, (OUTEDGE *) (curr->gelement), 
  1294.                       tree, current);
  1295.             exec_ckp;
  1296.             }
  1297.  
  1298.         relatives = find_elem(s, digraph, tree->lineno, current);
  1299.         break;
  1300.     
  1301.         case IN_TOKEN:
  1302.         case OUT_TOKEN:
  1303.         case PARENTS_TOKEN:
  1304.         case CHILDREN_TOKEN:
  1305.         case ANCESTORS_TOKEN:
  1306.         case DESCENDANTS_TOKEN:
  1307.         if (!curr->node)
  1308.         {
  1309.             exec_error = TRUE;
  1310.             print_ln(tree->lineno);
  1311.             print_cl(current);
  1312.             fprintf(stderr, "target of %s attribute not a node\n", 
  1313.                 reserved_wd[(int) tree->token - 
  1314.                     (int) FIRST_RESERVED_TOKEN]);
  1315.             return NULL;
  1316.         }
  1317.         else
  1318.         {
  1319.             relatives = 
  1320.               nrelfn[(int) tree->token - 
  1321.                  (int) FIRST_NREL_TOKEN]((NODE *) (curr->gelement),
  1322.                              digraph);
  1323.             break;
  1324.         }
  1325.     
  1326.         case SOURCE_TOKEN:
  1327.         case SINK_TOKEN:
  1328.         if (curr->node)
  1329.         {
  1330.             exec_error = TRUE;
  1331.             print_ln(tree->lineno);
  1332.             print_cl(current);
  1333.             fprintf(stderr, "target of %s attribute not an edge\n",
  1334.                 reserved_wd[(int) tree->token - 
  1335.                     (int) FIRST_RESERVED_TOKEN]);
  1336.             return NULL;
  1337.         }
  1338.         else
  1339.         {
  1340.             relatives =
  1341.               erelfn[(int) tree->token - 
  1342.                  (int) FIRST_EREL_TOKEN](
  1343.                 (OUTEDGE *) (curr->gelement), digraph, 
  1344.                 curr->from);
  1345.             break;
  1346.         }
  1347.  
  1348.         default:
  1349.         exec_error = TRUE;
  1350.         print_ln(tree->lineno);
  1351.         print_cl(current);
  1352.         fprintf(stderr, 
  1353.             "attribute designator tree can't be rooted with ");
  1354.         print_token(tree->token, TRUE);
  1355.         return NULL;
  1356.     }
  1357.  
  1358.     append_list(relatives, &head);
  1359.     }
  1360.  
  1361.     return head;
  1362. }
  1363.  
  1364. append_list(list1, list2)
  1365. LELEMENT *list1, **list2;
  1366.   /**
  1367.      add elements of list1 to list2.  Don't add duplicates.
  1368.      The discerning reader will note that this procedure isn't very efficient,
  1369.      but the lelement lists shouldn't be very long.
  1370.    **/
  1371. {
  1372.     LELEMENT *p, *n;
  1373.  
  1374.     for (; list1 != NULL; list1 = n)
  1375.     {
  1376.     n = list1->next;
  1377.  
  1378.     for (p = *list2; p != NULL; p = p->next)
  1379.     {
  1380.         if (p->gelement == list1->gelement)
  1381.         {
  1382.         goto next;
  1383.         }
  1384.     }
  1385.  
  1386.     list1->next = *list2;
  1387.     *list2 = list1;
  1388. next:
  1389.     ;
  1390.     }
  1391. }
  1392.  
  1393. append_list_dups(list1, list2)
  1394. LELEMENT *list1, **list2;
  1395.   /* Much like append_list above, but we'll add duplicates */
  1396. {
  1397.     LELEMENT *p;
  1398.  
  1399.     if (*list2 == NULL)
  1400.     {
  1401.     *list2 = list1;
  1402.     return;
  1403.     }
  1404.  
  1405.     for (p = *list2; p->next != NULL; p = p->next)
  1406.     {
  1407.     }
  1408.  
  1409.     p->next = list1;
  1410. }
  1411.     
  1412. set_nattr(digraph, n, tree, current, val)
  1413. DIGRAPH *digraph;
  1414. NODE *n;
  1415. PTREE *tree;
  1416. LELEMENT *current;
  1417. char *val;
  1418.   /**
  1419.      set n's value for the attribute represented by the tree.  If the 
  1420.      attribute doesn't exist, create it for all nodes.
  1421.    **/
  1422. {
  1423.     int i;
  1424.  
  1425.     exec_ck;
  1426.  
  1427.     if (tree == NULL)
  1428.     {
  1429.     exec_error = TRUE;
  1430.         print_cl(current);
  1431.     fprintf(stderr, "Node attribute tree is empty.\n");
  1432.     return;
  1433.     }
  1434.     else
  1435.     {
  1436.         switch(tree->token)
  1437.     {
  1438.         case STRING_TOKEN:
  1439.         for (i = 0; i < NNodeAttr(digraph); i++)
  1440.         {
  1441.             if (!strcmp(tree->val, NAttrName(digraph, i)))
  1442.             {
  1443.             dispose(NAttr(n, i));
  1444.             strsave(NAttr(n, i), val);
  1445.             pchanged = TRUE;
  1446.             return;
  1447.             }
  1448.         }
  1449.  
  1450. print_ln(tree->lineno);
  1451. print_cl(current);
  1452. fprintf(stderr, "Creating node attribute named %s\n", tree->val);
  1453.         create_nattr(digraph, tree->val);
  1454.         dispose(NAttr(n, NNodeAttr(digraph) - 1));
  1455.         strsave(NAttr(n, NNodeAttr(digraph) - 1), val);
  1456.         pnewattr = TRUE;
  1457.         break;
  1458.  
  1459.         case BRUSH_TOKEN:
  1460.         Brush(n) = string_to_brush(tree->val);
  1461.         case SHAPE_TOKEN:
  1462.         Shape(n) = string_to_shape(tree->val);
  1463.         case COLOR_TOKEN:
  1464.         Color(n) = string_to_color(tree->val);
  1465.         case DISPLAY_TOKEN:
  1466.         case COALESCER_TOKEN:
  1467.         case NODE_TOKEN:
  1468.         case EDGE_TOKEN:
  1469.         exec_error = TRUE;
  1470.         print_ln(tree->lineno);
  1471.         print_cl(current);
  1472.         fprintf(stderr, "Cannot set node attribute ");
  1473.         print_token(tree->token, TRUE);
  1474.         return;
  1475.         default:
  1476.         exec_error = TRUE;
  1477.         print_ln(tree->lineno);
  1478.             print_cl(current);
  1479.         fprintf(stderr, "Node attribute tree cannot be rooted with ");
  1480.         print_token(tree->token, TRUE);
  1481.         return;
  1482.     }
  1483.  
  1484.     pchanged = TRUE;
  1485.     }
  1486. }
  1487.  
  1488. char *get_nattr_val(digraph, n, tree, current)
  1489. DIGRAPH *digraph;
  1490. NODE *n;
  1491. PTREE *tree;
  1492. LELEMENT *current;
  1493.   /* return n's value for the attribute represented by the tree */
  1494. {
  1495.     int i;
  1496.  
  1497.     exec_ckp;
  1498.  
  1499.     if (tree == NULL)
  1500.     {
  1501.     exec_error = TRUE;
  1502.         print_cl(current);
  1503.     fprintf(stderr, "Node attribute tree is empty.\n");
  1504.     return NULL;
  1505.     }
  1506.     else
  1507.     {
  1508.         switch(tree->token)
  1509.     {
  1510.         case STRING_TOKEN:
  1511.         for (i = 0; i < NNodeAttr(digraph); i++)
  1512.         {
  1513.             if (!strcmp(tree->val, NAttrName(digraph, i)))
  1514.             {
  1515.             return NAttr(n, i);
  1516.             }
  1517.         }
  1518.  
  1519.         exec_error = TRUE;
  1520.         print_ln(tree->lineno);
  1521.             print_cl(current);
  1522.         fprintf(stderr, "No node attribute named %s\n", tree->val);
  1523.         return NULL;
  1524.         case BRUSH_TOKEN:
  1525.         return brush_to_string(Brush(n));
  1526.         case SHAPE_TOKEN:
  1527.         return brush_to_string(Shape(n));
  1528.         case COLOR_TOKEN:
  1529.         return brush_to_string(Color(n));
  1530.         case DISPLAY_TOKEN:
  1531.         return bool_to_string(Displayed(n));
  1532.         case COALESCER_TOKEN:
  1533.         return bool_to_string(Coalescer(n));
  1534.         case NODE_TOKEN:
  1535.         return bool_to_string(TRUE);
  1536.         case EDGE_TOKEN:
  1537.         return bool_to_string(FALSE);
  1538.         default:
  1539.         exec_error = TRUE;
  1540.         print_ln(tree->lineno);
  1541.             print_cl(current);
  1542.         fprintf(stderr, "Node attribute tree cannot be rooted with ");
  1543.         print_token(tree->token, TRUE);
  1544.         return NULL;
  1545.     }
  1546.     }
  1547. }
  1548.  
  1549. set_eattr(digraph, e, tree, current, val)
  1550. DIGRAPH *digraph;
  1551. OUTEDGE *e;
  1552. PTREE *tree;
  1553. LELEMENT *current;
  1554. char *val;
  1555.   /**
  1556.      set e's value for the attribute represented by the tree.  If the 
  1557.      attribute doesn't exist, create it for all edges.
  1558.    **/
  1559. {
  1560.     int i;
  1561.  
  1562.     exec_ck;
  1563.  
  1564.     if (tree == NULL)
  1565.     {
  1566.     exec_error = TRUE;
  1567.         print_cl(current);
  1568.     fprintf(stderr, "Edge attribute tree is empty.\n");
  1569.     return;
  1570.     }
  1571.     else
  1572.     {
  1573.         switch(tree->token)
  1574.     {
  1575.         case STRING_TOKEN:
  1576.         for (i = 0; i < NEdgeAttr(digraph); i++)
  1577.         {
  1578.             if (!strcmp(tree->val, EAttrName(digraph, i)))
  1579.             {
  1580.             dispose(EAttr(e, i));
  1581.             strsave(EAttr(e, i), val);
  1582.             pchanged = TRUE;
  1583.             return;
  1584.             }
  1585.         }
  1586.  
  1587. print_ln(tree->lineno);
  1588. print_cl(current);
  1589. fprintf(stderr, "Creating edge attribute named %s\n", tree->val);
  1590.         create_eattr(digraph, tree->val);
  1591.         dispose(EAttr(e, NEdgeAttr(digraph) - 1));
  1592.         strsave(EAttr(e, NEdgeAttr(digraph) - 1), val);
  1593.         pnewattr = TRUE;
  1594.         break;
  1595.  
  1596.         case BRUSH_TOKEN:
  1597.         Brush(e) = string_to_brush(tree->val);
  1598.         case COLOR_TOKEN:
  1599.         Color(e) = string_to_color(tree->val);
  1600.         case NODE_TOKEN:
  1601.         case EDGE_TOKEN:
  1602.         exec_error = TRUE;
  1603.         print_ln(tree->lineno);
  1604.         print_cl(current);
  1605.         fprintf(stderr, "Cannot set edge attribute ");
  1606.         print_token(tree->token, TRUE);
  1607.         return;
  1608.         default:
  1609.         exec_error = TRUE;
  1610.         print_ln(tree->lineno);
  1611.             print_cl(current);
  1612.         fprintf(stderr, "Edge attribute tree cannot be rooted with ");
  1613.         print_token(tree->token, TRUE);
  1614.         return;
  1615.     }
  1616.  
  1617.     pchanged = TRUE;
  1618.     }
  1619. }
  1620.  
  1621. char *get_eattr_val(digraph, e, tree, current)
  1622. DIGRAPH *digraph;
  1623. OUTEDGE *e;
  1624. PTREE *tree;
  1625. LELEMENT *current;
  1626.   /* return e's value for the attribute represented by the tree */
  1627. {
  1628.     int i;
  1629.  
  1630.     exec_ckp;
  1631.  
  1632.     if (tree == NULL)
  1633.     {
  1634.     exec_error = TRUE;
  1635.         print_cl(current);
  1636.     fprintf(stderr, "Edge attribute tree is empty.\n");
  1637.     return NULL;
  1638.     }
  1639.     else
  1640.     {
  1641.         switch(tree->token)
  1642.     {
  1643.         case STRING_TOKEN:
  1644.         for (i = 0; i < NEdgeAttr(digraph); i++)
  1645.         {
  1646.             if (!strcmp(tree->val, EAttrName(digraph, i)))
  1647.             {
  1648.             return EAttr(e, i);
  1649.             }
  1650.         }
  1651.  
  1652.         exec_error = TRUE;
  1653.         print_ln(tree->lineno);
  1654.             print_cl(current);
  1655.         fprintf(stderr, "No edge attribute named %s\n", tree->val);
  1656.         return NULL;
  1657.         case BRUSH_TOKEN:
  1658.         return brush_to_string(Brush(e));
  1659.         case COLOR_TOKEN:
  1660.         return brush_to_string(Color(e));
  1661.         case NODE_TOKEN:
  1662.         return bool_to_string(FALSE);
  1663.         case EDGE_TOKEN:
  1664.         return bool_to_string(TRUE);
  1665.         default:
  1666.         exec_error = TRUE;
  1667.         print_ln(tree->lineno);
  1668.             print_cl(current);
  1669.         fprintf(stderr, "Edge attribute tree cannot be rooted with ");
  1670.         print_token(tree->token, TRUE);
  1671.         return NULL;
  1672.     }
  1673.     }
  1674. }
  1675.  
  1676. create_nattr(digraph, name)
  1677. DIGRAPH *digraph;
  1678. char *name;
  1679.   /**
  1680.      create a new node attribute named name and initialize it to the NULL
  1681.      string for all nodes
  1682.    **/
  1683. {
  1684.     NODE *node;
  1685.  
  1686.     NNodeAttr(digraph)++;
  1687.  
  1688.     digraph->node_att_names = 
  1689.     (char **) realloc((char *) digraph->node_att_names,
  1690.               (NNodeAttr(digraph)) * sizeof(char *));
  1691.     strsave(NAttrName(digraph, NNodeAttr(digraph) - 1), name);
  1692.  
  1693.     all_nodes(digraph, node)
  1694.     loop
  1695.     node->attributes = 
  1696.         (char **) realloc((char *) node->attributes,
  1697.                   (NNodeAttr(digraph)) * sizeof(char *));
  1698.     strsave(NAttr(node, NNodeAttr(digraph) - 1), NULL_STRING);
  1699.     endloop;
  1700. }
  1701.     
  1702. create_eattr(digraph, name)
  1703. DIGRAPH *digraph;
  1704. char *name;
  1705.   /**
  1706.      Create a new edge attribute named name and initialize it to the NULL
  1707.      string for all edges
  1708.    **/
  1709. {
  1710.     NODE *node;
  1711.     OUTEDGE *edge;
  1712.  
  1713.     NEdgeAttr(digraph)++;
  1714.  
  1715.     digraph->edge_att_names = 
  1716.     (char **) realloc((char *) digraph->edge_att_names,
  1717.               (NEdgeAttr(digraph)) * sizeof(char *));
  1718.     strsave(EAttrName(digraph, NEdgeAttr(digraph) - 1), name);
  1719.  
  1720.     all_nodes(digraph, node)
  1721.     loop
  1722.     all_out_edges(node, edge)
  1723.     loop
  1724.         edge->attributes = 
  1725.             (char **) realloc((char *) edge->attributes,
  1726.                       (NEdgeAttr(digraph)) * sizeof(char *));
  1727.         strsave(EAttr(edge, NEdgeAttr(digraph) - 1), NULL_STRING);
  1728.     endloop;
  1729.     endloop;
  1730. }
  1731.     
  1732. BOOL get_bool_val(expr, digraph, current)
  1733. PTREE *expr;
  1734. DIGRAPH *digraph;
  1735. LELEMENT *current;
  1736.   /* all 'values' are actually strings for comparison's sake, even booleans */
  1737. {
  1738.     char *s;
  1739.  
  1740.     s = get_val(expr, digraph, current);
  1741.     exec_ckb;
  1742.  
  1743.     if (!strcmp(s, "true"))
  1744.     {
  1745.     return TRUE;
  1746.     }
  1747.     else if (!strcmp(s, "false"))
  1748.     {
  1749.         return FALSE;
  1750.     }
  1751.     else
  1752.     {
  1753.     exec_error = TRUE;
  1754.     print_ln(expr->lineno);
  1755.         print_cl(current);
  1756.     fprintf(stderr, "%s is not a boolean value.\n", s);
  1757.     return FALSE;
  1758.     }
  1759. }
  1760.  
  1761. LELEMENT *get_inedges(node, digraph)
  1762. NODE *node;
  1763. DIGRAPH *digraph;
  1764.   /**
  1765.      get the inedges of a node.  As noted elsewhere, some inedges are actually
  1766.      outedges and vice versa, so look at both lists
  1767.    **/
  1768. {
  1769.     LELEMENT *head, *curr, *c;
  1770.  
  1771.     head = NULL;
  1772.  
  1773.     curr = iedges(digraph, node);
  1774.  
  1775.     for (; curr != NULL; curr = c)
  1776.     {
  1777.     c = curr->next;
  1778.  
  1779.     if (!Edge_reversed((OUTEDGE *) curr->gelement))
  1780.     {
  1781.         curr->next = head;
  1782.         head = curr;
  1783.     }
  1784.     else
  1785.     {
  1786.        dispose(curr);
  1787.     }
  1788.     }
  1789.  
  1790.     curr = oedges(digraph, node);
  1791.  
  1792.     for (; curr != NULL; curr = c)
  1793.     {
  1794.     c = curr->next;
  1795.  
  1796.     if (Edge_reversed((OUTEDGE *) curr->gelement))
  1797.     {
  1798.         curr->next = head;
  1799.         head = curr;
  1800.     }
  1801.     else
  1802.     {
  1803.         dispose(curr);
  1804.     }
  1805.     }
  1806.  
  1807.     return head;
  1808. }
  1809.  
  1810. LELEMENT *get_outedges(node, digraph)
  1811. NODE *node;
  1812. DIGRAPH *digraph;
  1813.   /* get the outedges of a node.  The comment for get_inedges applies here */
  1814. {
  1815.     LELEMENT *head, *curr, *c;
  1816.  
  1817.     head = NULL;
  1818.  
  1819.     curr = oedges(digraph, node);
  1820.  
  1821.     for (; curr != NULL; curr = c)
  1822.     {
  1823.     c = curr->next;
  1824.  
  1825.     if (!Edge_reversed((OUTEDGE *) curr->gelement))
  1826.     {
  1827.         curr->next = head;
  1828.         head = curr;
  1829.     }
  1830.     else
  1831.     {
  1832.         dispose(curr);
  1833.     }
  1834.     }
  1835.  
  1836.     curr = iedges(digraph, node);
  1837.  
  1838.     for (; curr != NULL; curr = c)
  1839.     {
  1840.     c = curr->next;
  1841.  
  1842.     if (Edge_reversed((OUTEDGE *) curr->gelement))
  1843.     {
  1844.         curr->next = head;
  1845.         head = curr;
  1846.     }
  1847.     else
  1848.     {
  1849.         dispose(curr);
  1850.     }
  1851.     }
  1852.  
  1853.     return head;
  1854. }
  1855.  
  1856. LELEMENT *get_parents(node, digraph)
  1857. NODE *node;
  1858. DIGRAPH *digraph;
  1859.   /* get the parents of a node.  See the comment for get_inedges */
  1860. {
  1861.     LELEMENT *head, *curr, *c, *p;
  1862.     OUTEDGE *oe;
  1863.     SET *visited;
  1864.     NODE *tonode;
  1865.  
  1866.     head = NULL;
  1867.     init_set(visited);
  1868.  
  1869.     p = iedges(digraph, node);
  1870.  
  1871.     for (c = p; p != NULL; p = p->next)
  1872.     {
  1873.     oe = (OUTEDGE *) p->gelement;
  1874.  
  1875.     if (!in(visited, Vno(p->from)) && !Edge_reversed(oe))
  1876.     {
  1877.         add_element(visited, Vno(p->from));
  1878.         new(curr, LELEMENT);
  1879.         curr->node = TRUE;
  1880.         curr->gelement = (GELEMENT) (p->from);
  1881.         curr->from = NULL;
  1882.         curr->next = head;
  1883.         head = curr;
  1884.     }
  1885.     }
  1886.  
  1887.     dispose_lelem_list(c);
  1888.     p = oedges(digraph, node);
  1889.  
  1890.     for (c = p; p != NULL; p = p->next)
  1891.     {
  1892.     oe = (OUTEDGE *) p->gelement;
  1893.     tonode = visual(digraph, Node(digraph, To_vno(oe)));
  1894.  
  1895.     if (!in(visited, Vno(tonode)) && Edge_reversed(oe))
  1896.     {
  1897.         add_element(visited, Vno(tonode));
  1898.         new(curr, LELEMENT);
  1899.         curr->node = TRUE;
  1900.         curr->gelement = (GELEMENT) tonode;
  1901.         curr->from = NULL;
  1902.         curr->next = head;
  1903.         head = curr;
  1904.     }
  1905.     }
  1906.  
  1907.     dispose_lelem_list(c);
  1908.     dispose(visited);
  1909.     return head;
  1910. }
  1911.  
  1912. LELEMENT *get_children(node, digraph)
  1913. NODE *node;
  1914. DIGRAPH *digraph;
  1915.   /* get the children of a node.  See the comment for get_inedges */
  1916. {
  1917.     LELEMENT *head, *curr, *c, *p;
  1918.     OUTEDGE *oe;
  1919.     SET *visited;
  1920.     NODE *tonode;
  1921.  
  1922.     head = NULL;
  1923.     init_set(visited);
  1924.  
  1925.     p = oedges(digraph, node);
  1926.  
  1927.     for (c = p; p != NULL; p = p->next)
  1928.     {
  1929.     oe = (OUTEDGE *) p->gelement;
  1930.     tonode = visual(digraph, Node(digraph, To_vno(oe)));
  1931.  
  1932.     if (!in(visited, Vno(tonode)) && !Edge_reversed(oe))
  1933.     {
  1934.         add_element(visited, Vno(tonode));
  1935.         new(curr, LELEMENT);
  1936.         curr->node = TRUE;
  1937.         curr->gelement = (GELEMENT) tonode;
  1938.         curr->from = NULL;
  1939.         curr->next = head;
  1940.         head = curr;
  1941.     }
  1942.     }
  1943.  
  1944.     dispose_lelem_list(c);
  1945.     p = iedges(digraph, node);
  1946.  
  1947.     for (c = p; p != NULL; p = p->next)
  1948.     {
  1949.     oe = (OUTEDGE *) p->gelement;
  1950.  
  1951.     if (!in(visited, Vno(p->from)) && Edge_reversed(oe))
  1952.     {
  1953.         add_element(visited, Vno(p->from));
  1954.         curr->node = TRUE;
  1955.         curr->gelement = (GELEMENT) p->from;
  1956.         curr->from = NULL;
  1957.         curr->next = head;
  1958.         head = curr;
  1959.     }
  1960.     }
  1961.  
  1962.     dispose_lelem_list(c);
  1963.     dispose(visited);
  1964.     return head;
  1965. }
  1966.  
  1967. get_tc(node, digraph, visited, ppred, head)
  1968. NODE *node;
  1969. DIGRAPH *digraph;
  1970. SET *visited;
  1971. BOOL ppred;
  1972. LELEMENT **head;
  1973.   /**
  1974.      Find the transitive predecessor (if ppred is true) or successor (if pred
  1975.      is false) closure of the digraph from node.  A recursive procedure; the
  1976.      visited variable indicates if we've visited the node before.  head is
  1977.      a list of the nodes in the closure so far.
  1978.  
  1979.      Uses the depth-first search algorithm we know and love.
  1980.  
  1981.      See get_inedges for another detail
  1982.    **/
  1983. {
  1984.     LELEMENT *curr, *c, *p;
  1985.     NODE *neighbor;
  1986.     OUTEDGE *oe;
  1987.  
  1988.     p = oedges(digraph, node);
  1989.  
  1990.     for (c = p; p != NULL; p = p->next)
  1991.     {
  1992.     oe = (OUTEDGE *) p->gelement;
  1993.     neighbor = visual(digraph, Node(digraph, To_vno(oe)));
  1994.  
  1995.     if (!in(visited, Vno(neighbor)) && Edge_reversed(oe) == ppred)
  1996.     {
  1997.         add_element(visited, Vno(neighbor));
  1998.  
  1999.             new(curr, LELEMENT);
  2000.             curr->node = TRUE;
  2001.             curr->gelement = (GELEMENT) neighbor;
  2002.             curr->from = NULL;
  2003.             curr->next = *head;
  2004.             *head = curr;
  2005.     
  2006.         get_tc(neighbor, digraph, visited, ppred, head);
  2007.           /* recursion (whee!) */
  2008.     }
  2009.     }
  2010.  
  2011.     dispose_lelem_list(c);
  2012.     p = iedges(digraph, node);
  2013.  
  2014.     for (c = p; p != NULL; p = p->next)
  2015.     {
  2016.     oe = (OUTEDGE *) p->gelement;
  2017.  
  2018.     if (!in(visited, Vno(p->from)) && !Edge_reversed(oe) == ppred)
  2019.     {
  2020.         neighbor = p->from;
  2021.         add_element(visited, Vno(neighbor));
  2022.     
  2023.             new(curr, LELEMENT);
  2024.             curr->node = TRUE;
  2025.             curr->gelement = (GELEMENT) neighbor;
  2026.             curr->from = NULL;
  2027.             curr->next = *head;
  2028.             *head = curr;
  2029.     
  2030.         get_tc(neighbor, digraph, visited, ppred, head);
  2031.         /* recursion (whee!) */
  2032.     }
  2033.     }
  2034.  
  2035.     dispose_lelem_list(c);
  2036. }
  2037.     
  2038. LELEMENT *get_ancestors(node, digraph)
  2039. NODE *node;
  2040. DIGRAPH *digraph;
  2041. {
  2042.     SET *visited;
  2043.     LELEMENT *result = NULL;
  2044.  
  2045.     init_set(visited);
  2046.     get_tc(node, digraph, visited, TRUE, &result);
  2047.     dispose(visited);
  2048.     return result;
  2049. }
  2050.  
  2051. LELEMENT *get_descendants(node, digraph)
  2052. NODE *node;
  2053. DIGRAPH *digraph;
  2054. {
  2055.     SET *visited;
  2056.     LELEMENT *result = NULL;
  2057.  
  2058.     init_set(visited);
  2059.     get_tc(node, digraph, visited, FALSE, &result);
  2060.     dispose(visited);
  2061.     return result;
  2062. }
  2063.  
  2064. LELEMENT *get_source(edge, digraph, from)
  2065. OUTEDGE *edge;
  2066. DIGRAPH *digraph;
  2067. NODE *from;
  2068.   /* get the source of an edge.  Be sure to check if it's reversed */
  2069. {
  2070.     LELEMENT *result;
  2071.  
  2072.     new(result, LELEMENT);
  2073.     result->from = NULL;
  2074.     result->node = TRUE;
  2075.     result->next = NULL;
  2076.  
  2077.     if (Edge_reversed(edge))
  2078.     {
  2079.         result->gelement = (GELEMENT) visual(digraph, 
  2080.                          Node(digraph, To_vno(edge)));
  2081.     }
  2082.     else
  2083.     {
  2084.         result->gelement = (GELEMENT) from;
  2085.     }
  2086.     
  2087.     return result;
  2088. }
  2089.  
  2090. LELEMENT *get_sink(edge, digraph, from)
  2091. OUTEDGE *edge;
  2092. DIGRAPH *digraph;
  2093. NODE *from;
  2094.   /* get the sink of an edge.  Be sure to check if it's reversed */
  2095. {
  2096.     LELEMENT *result;
  2097.  
  2098.     new(result, LELEMENT);
  2099.     result->from = NULL;
  2100.     result->node = TRUE;
  2101.     result->next = NULL;
  2102.  
  2103.     if (Edge_reversed(edge))
  2104.     {
  2105.     result->gelement = (GELEMENT) from;
  2106.     }
  2107.     else
  2108.     {
  2109.     result->gelement = (GELEMENT) visual(digraph, 
  2110.                          Node(digraph, To_vno(edge)));
  2111.     }
  2112.  
  2113.     return result;
  2114. }
  2115.  
  2116. LELEMENT *all_nodes_edges(digraph)
  2117. DIGRAPH *digraph;
  2118.   /** 
  2119.      make an lelement list of all the nodes and edges in the digraph
  2120.      we are concerned about
  2121.    **/
  2122. {
  2123.     NODE *node;
  2124.     LELEMENT *head, *curr;
  2125.  
  2126.     head = NULL;
  2127.  
  2128.     each_pred_node(digraph, node)
  2129.     loop
  2130.     if (Is_dummy(node))    /* don't care about dummies */
  2131.     {
  2132.         continue;
  2133.     }
  2134.  
  2135.     new(curr, LELEMENT);
  2136.     curr->node = TRUE;
  2137.     curr->gelement = (GELEMENT) node;
  2138.     curr->from = NULL;
  2139.     curr->next = head;
  2140.     head = curr;
  2141.  
  2142.         append_list_dups(oedges(digraph, node), &head);
  2143.                 /* add relevant outedges */
  2144.     endloop;
  2145.  
  2146.     return head;
  2147. }
  2148.  
  2149. LELEMENT *oedges(digraph, inode)
  2150. DIGRAPH *digraph;
  2151. NODE *inode;
  2152.   /**
  2153.      Return an lelement list of all the outedges of inode
  2154.      inode may or may not be coalescer.  
  2155.    **/
  2156. {
  2157.     VNO nvno;
  2158.     OUTEDGE *outedge;
  2159.     LELEMENT *curr, *result = NULL;
  2160.     NODE *to, *node;
  2161.  
  2162.     if (Coalescer(inode))
  2163.     {
  2164.     each_element(Expansion(inode), nvno)
  2165.     loop
  2166.         append_list_dups(oedges(digraph, Node(digraph, nvno)), &result);
  2167.     endloop;
  2168.     }
  2169.     else
  2170.     {
  2171.     all_out_edges(inode, outedge)
  2172.     loop
  2173.         to = visual(digraph, Node(digraph, To_vno(outedge)));
  2174.         node = visual(digraph, inode);
  2175.           /* get the visible endpoint of the edge */
  2176.  
  2177.         if (Displayed(to) || !ignoreHidden)
  2178.         {
  2179.             new(curr, LELEMENT);
  2180.             curr->node = FALSE;
  2181.             curr->gelement = (GELEMENT) outedge;
  2182.             curr->from = node;
  2183.             curr->next = result;
  2184.             result = curr;
  2185.         }
  2186.     endloop;
  2187.     }
  2188.  
  2189.     return result;
  2190. }
  2191.  
  2192. LELEMENT *iedges(digraph, node)
  2193. DIGRAPH *digraph;
  2194. NODE *node;
  2195.   /**
  2196.      Return an lelement list of all the outedges with inode as their
  2197.      to_vno.
  2198.      inode may or may not be coalescer.  
  2199.    **/
  2200. {
  2201.     VNO nvno;
  2202.     INEDGE *inedge;
  2203.     LELEMENT *curr, *result = NULL;
  2204.     NODE *from, *ifrom;
  2205.  
  2206.     if (Coalescer(node))
  2207.     {
  2208.     each_element(Expansion(node), nvno)
  2209.     loop
  2210.         append_list_dups(iedges(digraph, Node(digraph, nvno)), &result);
  2211.     endloop;
  2212.     }
  2213.     else
  2214.     {
  2215.     all_in_edges(node, inedge)
  2216.     loop
  2217.         ifrom = Node(digraph, From_vno(inedge));
  2218.         from = visual(digraph, ifrom);
  2219.  
  2220.         if (Displayed(from) || !ignoreHidden)
  2221.         {
  2222.             new(curr, LELEMENT);
  2223.             curr->node = FALSE;
  2224.             curr->gelement = (GELEMENT) get_edge(digraph, ifrom, node,
  2225.                              Ord(inedge));
  2226.             curr->from = from;
  2227.             curr->next = result;
  2228.             result = curr;
  2229.         }
  2230.     endloop;
  2231.     }
  2232.  
  2233.     return result;
  2234. }
  2235.  
  2236. print_ln(n)
  2237. int n;
  2238.   /* prints a line number */
  2239. {
  2240.     fprintf(stderr, "on line #%d:  ", n);
  2241. }
  2242.  
  2243. print_cl(elem)
  2244. LELEMENT *elem;
  2245.   /* prints the label of the current node/edge */
  2246. {
  2247.     if (elem->node)
  2248.     {
  2249.     fprintf(stderr, "node \`%s\':  ", Text((NODE *) elem->gelement));
  2250.     }
  2251.     else
  2252.     {
  2253.     fprintf(stderr, "edge \`%s\':  ", Label((OUTEDGE *) elem->gelement));
  2254.     }
  2255. }
  2256.  
  2257. char **copy_attr();
  2258. VERTEX *insert_vertex();
  2259. NODE *insert_node();
  2260.  
  2261. coalesce(digraph, n1, n2)
  2262. DIGRAPH *digraph;
  2263. NODE *n1, *n2;
  2264.   /**
  2265.      coalesce n1 to n2 (n2 becomes the distinguished node)
  2266.    **/
  2267. {
  2268.     NODE *cr_node;
  2269.     VERTEX *cr_vertex;
  2270.     char name[MAXBUFFER];  /* name of the coalescer */
  2271.     char** attr;
  2272.  
  2273.     if (Coalescer(n2))
  2274.     {
  2275.     cr_node = n2;
  2276.     }
  2277.     else
  2278.     {
  2279.         strncpy(name, UniqueName(), MAXBUFFER);
  2280.         attr = copy_attr(n2->attributes, NNodeAttr(digraph));
  2281.  
  2282.           /* create a coalescer. */
  2283.         cr_vertex = insert_vertex(digraph, name);
  2284.         cr_node = insert_node(digraph, cr_vertex, attr, Shape(n2),
  2285.                           Brush(n2), Color(n2), Displayed(n2), 0, 0, 
  2286.                  NORMAL);
  2287.     Coalescer(cr_node) = TRUE;
  2288.     add_to_coalescer(digraph, n2, cr_node);
  2289.     }
  2290.  
  2291.     if (n1 == cr_node)
  2292.     {
  2293.     return;        /* nop to add a coalesced node to itself */
  2294.     }
  2295.  
  2296.     add_to_coalescer(digraph, n1, cr_node);
  2297. }
  2298.  
  2299. add_to_coalescer(digraph, n, cn)
  2300. DIGRAPH *digraph;
  2301. NODE *n, *cn;
  2302.   /**
  2303.      add n to the coalescer node cn 
  2304.  
  2305.      This doesn't work correctly; if n has edges to a coalescer node, the 
  2306.      elements of that node must be changed so their ante/succ sets include
  2307.      cn instead of n.  This would cause troubles were the coalescer to
  2308.      be expanded.
  2309.  
  2310.      But, if that were to take place, you couldn't access the elements
  2311.      of the expansions in this set of predicates.  And after every set of 
  2312.      predicates which include an expand, the ante/succ sets are recomputed 
  2313.      (in relay.c).  So it works for now.
  2314.  
  2315.      To fix it, the remove_element, add_element calls would have
  2316.      to recurse for every element in the expansion if the neighbor 
  2317.      were a coalescer.  But you also have to watch for hidden nodes.
  2318.      Which is why I leave it to relay.c
  2319.    **/
  2320. {
  2321.     VNO cr_vno, vno, succ_vno, ante_vno;  /* vertex numbers */
  2322.  
  2323.     cr_vno = Vno(cn);
  2324.     vno = Vno(n);
  2325.     Coalescer_vno(n) = cr_vno;   /* this node has a coalescer */
  2326.     Coalesced(n) = TRUE;
  2327.     Displayed(n) = FALSE;
  2328.     add_element(Expansion(cn), vno);
  2329.  
  2330.       /**
  2331.      add the successors and predecessors of this node to the coalescer
  2332.      node, but don't include elements in the coalescer node 
  2333.        **/
  2334.     union(Succ_set(cn), Succ_set(n));
  2335.     union(Ante_set(cn), Ante_set(n));
  2336.     difference(Succ_set(cn), Expansion(cn));
  2337.     difference(Ante_set(cn), Expansion(cn));
  2338.  
  2339.     each_element(Succ_set(n), succ_vno)  
  2340.       /* fix succedents to point to the coalescer */
  2341.     loop
  2342.     remove_element(Ante_set(Node(digraph, succ_vno)), vno);
  2343.     add_element(Ante_set(Node(digraph, succ_vno)), cr_vno);
  2344.     endloop
  2345.  
  2346.     each_element(Ante_set(n), ante_vno) 
  2347.       /* fix antecedents to point to the coalescer*/
  2348.     loop
  2349.         remove_element(Succ_set(Node(digraph, ante_vno)), vno);
  2350.         add_element(Succ_set(Node(digraph, ante_vno)), cr_vno);
  2351.     endloop
  2352.  
  2353.       /* make sure the coalescer isn't its own successor or predecessor */
  2354.     remove_element(Succ_set(cn), cr_vno);
  2355.     remove_element(Ante_set(cn), cr_vno);
  2356. } /* add_to_coalescer */
  2357.  
  2358. expand(digraph, n1)
  2359. DIGRAPH *digraph;
  2360. NODE *n1;
  2361.   /**
  2362.      the inverse of coalescing.  n1 is a coalescer.  Break it up into
  2363.      its consituent parts
  2364.  
  2365.      Also doesn't quite get the ante/succ sets right.  See add_to_coalescer.
  2366.  
  2367.      Need to change the add_element calls below to also add nvno to
  2368.      the proper succ/ante sets if the neighbors are coalescer nodes.  And
  2369.      watch out for hidden nodes.  This is best left to relay.c
  2370.    **/
  2371. {
  2372.     VNO nvno, succ_vno, ante_vno, cr_vno;
  2373.     NODE *n;
  2374.  
  2375.     cr_vno = Vno(n1);
  2376.  
  2377.     each_element(Expansion(n1), nvno)
  2378.     loop
  2379.     n = Node(digraph, nvno);
  2380.     Coalescer_vno(n) = -1;
  2381.     Coalesced(n) = FALSE;
  2382.     Displayed(n) = TRUE;
  2383.  
  2384.           /**
  2385.          add this node back to the successor and predecessor set of its
  2386.          neighbors
  2387.        **/
  2388.         each_element(Succ_set(n), succ_vno)  
  2389.         loop
  2390.         add_element(Ante_set(Node(digraph, succ_vno)), nvno);
  2391.         endloop
  2392.     
  2393.         each_element(Ante_set(n), ante_vno) 
  2394.         loop
  2395.             add_element(Succ_set(Node(digraph, ante_vno)), nvno);
  2396.         endloop
  2397.     endloop;
  2398.  
  2399.       /**
  2400.      delete the coalescer node from the successor and predecessor
  2401.      sets of other nodes
  2402.        **/
  2403.     each_element(Succ_set(n1), succ_vno)
  2404.     loop
  2405.     remove_element(Ante_set(Node(digraph, succ_vno)), cr_vno);
  2406.     endloop;
  2407.  
  2408.     each_element(Ante_set(n1), ante_vno)
  2409.     loop
  2410.         remove_element(Succ_set(Node(digraph, ante_vno)), cr_vno);
  2411.     endloop
  2412.  
  2413.       /**
  2414.      if we don't set Coalescer to false, dispose_node will
  2415.      delete every element in the coalescer's expansion set
  2416.        **/
  2417.     Coalescer(n1) = FALSE;        
  2418.     dispose_node(digraph, n1);
  2419. }
  2420.  
  2421. NODE *visual(digraph, n)
  2422. DIGRAPH *digraph;
  2423. NODE *n;
  2424.   /* find the coalescer node which represents n (if any) */
  2425. {
  2426.     while (Coalesced(n))
  2427.     {
  2428.     n = Node(digraph, Coalescer_vno(n));
  2429.     }
  2430.  
  2431.     return n;
  2432. }
  2433.